package ru.pb.game.utils;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import ru.pb.game.objects.model.GameObject;

/**
 * @author: Felixx
 */
public class Utils
{
	public static final double HEADINGS_IN_PI = 10430.378350470452724949566316381;

	public static boolean checkLongOverflow(double d)
	{
		return (d > Long.MAX_VALUE) || (d < Long.MIN_VALUE);
	}

	public static boolean checkLongOverflowMul(long l1, long l2)
	{
		return checkLongOverflow(1 * l1 * l2);
	}

	public static boolean checkLongOverflowAdd(long l1, long l2)
	{
		return checkLongOverflow(1 * l1 + l2);
	}

	public static boolean checkLongOverflowAdd(double l1, long l2)
	{
		return checkLongOverflow(l1 + l2);
	}

	@Deprecated
	public static double calculateAngleFrom(GameObject obj1, GameObject obj2)
	{
		// return calculateAngleFrom(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
		return 0;
	}

	public static double calculateAngleFrom(int obj1X, int obj1Y, int obj2X, int obj2Y)
	{
		double angleTarget = Math.toDegrees(Math.atan2(obj1Y - obj2Y, obj1X - obj2X));
		if(angleTarget <= 0)
		{
			angleTarget += 360;
		}
		return angleTarget;
	}

	/**
	 * Проверяет строку на соответсвие регулярному выражению
	 * 
	 * @param text
	 *            Строка-источник
	 * @param template
	 *            Шаблон для поиска
	 * @return true в случае соответвия строки шаблону
	 */
	public static boolean isMatchingRegexp(String text, String template)
	{
		Pattern pattern = null;
		try
		{
			pattern = Pattern.compile(template);
		}
		catch(PatternSyntaxException e) // invalid template
		{
			//
		}
		if(pattern == null)
		{
			return false;
		}
		Matcher regexp = pattern.matcher(text);
		return regexp.matches();
	}

	public static boolean isAlphaNumeric(String text)
	{
		if(text == null)
			return false;

		boolean result = true;
		char[] chars = text.toCharArray();
		for(char aChar : chars)
		{
			if( !Character.isLetterOrDigit(aChar))
			{
				result = false;
				break;
			}
		}
		return result;
	}

	public static double calculateDistance(int x1, int y1, int z1, int x2, int y2)
	{
		return calculateDistance(x1, y1, 0, x2, y2, 0, false);
	}

	public static double calculateDistance(int x1, int y1, int z1, int x2, int y2, int z2, boolean includeZAxis)
	{
		double dx = x1 - x2;
		double dy = y1 - y2;

		if(includeZAxis)
		{
			int dz = z1 - z2;
			return Math.sqrt(dx * dx + dy * dy + dz * dz);
		}
		return Math.sqrt(dx * dx + dy * dy);
	}

	@Deprecated
	public static double calculateDistance(GameObject obj1, GameObject obj2, boolean includeZAxis)
	{
		if(obj1 == null || obj2 == null)
		{
			return 1000000;
		}
		// return calculateDistance(obj1.getX(), obj1.getY(), obj1.getZ(), obj2.getX(), obj2.getY(), obj2.getZ(), includeZAxis);
		return 0;
	}

	public static double convertHeadingToDegree(int heading)
	{
		double angle = heading / 182.044444444;
		if(angle == 0)
		{
			angle = 360;
		}
		return angle;
	}

	public static double convertHeadingToRadian(int heading)
	{
		return Math.toRadians(convertHeadingToDegree(heading) - 90);
	}

	public static int convertDegreeToClientHeading(double degree)
	{
		if(degree < 0)
		{
			degree = 360 + degree;
		}
		return (int) (degree * 182.044444444);
	}

	public static boolean checkIfInRange(int range, int x1, int y1, int x2, int y2)
	{
		return checkIfInRange(range, x1, y1, 0, x2, y2, 0, false);
	}

	public static boolean checkIfInRange(int range, int x1, int y1, int z1, int x2, int y2, int z2, boolean includeZAxis)
	{
		int dx = x1 - x2;
		int dy = y1 - y2;

		if(includeZAxis)
		{
			int dz = z1 - z2;
			return dx * dx + dy * dy + dz * dz <= range * range;
		}
		return dx * dx + dy * dy <= range * range;
	}

	@Deprecated
	public static boolean checkIfInRange(int range, GameObject obj1, GameObject obj2, boolean includeZAxis)
	{
		if(obj1 == null || obj2 == null)
		{
			return false;
		}
		// return checkIfInRange(range, obj1.getX(), obj1.getY(), obj1.getZ(), obj2.getX(), obj2.getY(), obj2.getZ(), includeZAxis);
		return false;
	}

	public static int packInt(int[] a, int bits) throws Exception
	{
		int m = 32 / bits;
		if(a.length > m)
		{
			throw new Exception("Overflow");
		}

		int result = 0;
		int next;
		int mval = (int) Math.pow(2, bits);
		for(int i = 0; i < m; i++)
		{
			result <<= bits;
			if(a.length > i)
			{
				next = a[i];
				if(next >= mval || next < 0)
				{
					throw new Exception("Overload, value is out of range");
				}
			}
			else
			{
				next = 0;
			}
			result += next;
		}
		return result;
	}

	public static long packLong(int[] a, int bits) throws Exception
	{
		int m = 64 / bits;
		if(a.length > m)
		{
			throw new Exception("Overflow");
		}

		long result = 0;
		int next;
		int mval = (int) Math.pow(2, bits);
		for(int i = 0; i < m; i++)
		{
			result <<= bits;
			if(a.length > i)
			{
				next = a[i];
				if(next >= mval || next < 0)
				{
					throw new Exception("Overload, value is out of range");
				}
			}
			else
			{
				next = 0;
			}
			result += next;
		}
		return result;
	}

	public static int[] unpackInt(int a, int bits)
	{
		int m = 32 / bits;
		int mval = (int) Math.pow(2, bits);
		int[] result = new int[m];
		int next;
		for(int i = m; i > 0; i--)
		{
			next = a;
			a = a >> bits;
			result[i - 1] = next - a * mval;
		}
		return result;
	}

	public static int[] unpackLong(long a, int bits)
	{
		int m = 64 / bits;
		int mval = (int) Math.pow(2, bits);
		int[] result = new int[m];
		long next;
		for(int i = m; i > 0; i--)
		{
			next = a;
			a = a >> bits;
			result[i - 1] = (int) (next - a * mval);
		}
		return result;
	}

	@Deprecated
	public static int calculateHeadingFrom(GameObject obj1, GameObject obj2)
	{
		// return calculateHeadingFrom(obj1.getX(), obj1.getY(), obj2.getX(), obj2.getY());
		return 0;
	}

	public static int calculateHeadingFrom(int obj1X, int obj1Y, int obj2X, int obj2Y)
	{
		double angleTarget = Math.toDegrees(Math.atan2(obj2Y - obj1Y, obj2X - obj1X));
		if(angleTarget < 0)
			angleTarget = 360 + angleTarget;
		return (int) (angleTarget * 182.044444444);
	}

	// @Deprecated
	// public static int calcHeading(GameObject obj, Location dest)
	// {
	// if(dest == null)
	// return 0;
	//
	// if(Math.abs(obj.getX() - dest.x) == 0 && Math.abs(obj.getY() - dest.y) == 0)
	// return obj.getHeading();
	//
	// return calcHeading(obj, dest.getX(), dest.getY());
	// }
	@Deprecated
	public static int calcHeading(GameObject obj, int x_dest, int y_dest)
	{
		// return (int) (Math.atan2(obj.getY() - y_dest, obj.getX() - x_dest) * HEADINGS_IN_PI) + 32768;
		return 0;
	}

	public static int percent(int max, int percent)
	{
		return (max * percent) / 100;
	}

	public static short percent(short max, short percent)
	{
		return (short) ((max * percent) / 100);
	}

	public static int limit(int min, long value, int max)
	{
		return (int) Math.max(min, Math.min(value, max));
	}

	public static int limit(int min, int value, int max)
	{
		return Math.max(min, Math.min(value, max));
	}

	public static int limit(int min, double value, int max)
	{
		return (int) Math.max(min, Math.min(value, max));
	}

	public static int limit(int min, float value, int max)
	{
		return (int) Math.max(min, Math.min(value, max));
	}

	public static long limit(long min, long value, long max)
	{
		return Math.max(min, Math.min(value, max));
	}

	public static long limit(long min, double value, long max)
	{
		return (long) Math.max(min, Math.min(value, max));
	}

	public static long limit(long min, float value, long max)
	{
		return (long) Math.max(min, Math.min(value, max));
	}

	public static float limit(float min, double value, float max)
	{
		return (float) Math.max(min, Math.min(value, max));
	}

	public static float limit(float min, float value, float max)
	{
		return Math.max(min, Math.min(value, max));
	}

	public static double limit(double min, double value, double max)
	{
		return Math.max(min, Math.min(value, max));
	}

	public static byte min(byte a, byte b)
	{
		return (a <= b) ? a : b;
	}
}